home *** CD-ROM | disk | FTP | other *** search
/ mail.altrad.com / 2015.02.mail.altrad.com.tar / mail.altrad.com / TEST / office deutch / INFOPATH.NL-NL / INFLR.CAB / PURCHORD.XSN_1043 / script.js < prev    next >
Text File  |  2006-11-12  |  26KB  |  836 lines

  1. /*
  2.  * This file contains functions for data validation and form-level events.
  3.  * Because the functions are referenced in the form definition (.xsf) file, 
  4.  * it is recommended that you do not modify the name of the function,
  5.  * or the name and number of arguments.
  6.  *
  7. */
  8.  
  9. // The following line is created by Microsoft Office InfoPath to define the
  10. // prefixes for all the known namespaces in the main XML data file.
  11. // Any modification to the form files made outside of InfoPath
  12. // will not be autmatically updated.
  13. //<namespacesDefinition>
  14. XDocument.DOM.setProperty("SelectionNamespaces", 'xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD" xmlns:po="http://schemas.microsoft.com/office/infopath/2003/sample/PurchaseOrder"');
  15. //</namespacesDefinition>
  16.  
  17. var g_xmlDeliverTo;
  18.  
  19. /*------------------------------------------------------------------------------
  20.     This function handler is generated and managed automatically.
  21.     Do not rename the function or alter its parameter list.
  22. ------------------------------------------------------------------------------*/
  23. function XDocument::OnLoad(oEvent)
  24. {
  25.     // Avoid side effects when DOM is read only.
  26.     if (XDocument.IsDOMReadOnly)
  27.         return;
  28.  
  29.     var xmlSolutionDOM = XDocument.Solution.DOM;
  30.     xmlSolutionDOM.setProperty("SelectionNamespaces", 'xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD" xmlns:po="http://schemas.microsoft.com/office/infopath/2003/sample/PurchaseOrder"');
  31.  
  32.     g_xmlDeliverTo = xmlSolutionDOM.documentElement.selectSingleNode("//po:deliverTo");
  33.     if (g_xmlDeliverTo != null)
  34.     {
  35.         g_xmlDeliverTo = g_xmlDeliverTo.cloneNode(true);
  36.     }
  37.  
  38.     // Initialize the date
  39.     initializeNodeValue("/po:purchaseOrder/po:dateOrdered", getDateString());
  40. }
  41.  
  42.  
  43. /*------------------------------------------------------------------------------
  44.     This function handler is generated and managed automatically.
  45.     Do not rename the function or alter its parameter list.
  46. ------------------------------------------------------------------------------*/
  47. function msoxd__deliverTo::OnAfterChange(oEvent)
  48. {
  49.     // Avoid side effects when DOM is read only.
  50.     if (XDocument.IsDOMReadOnly)
  51.         return;
  52.  
  53.     var xmlDeliverTo = XDocument.DOM.documentElement.selectSingleNode("/po:purchaseOrder/po:deliverTo");
  54.     var xmlShipToSubmitter = XDocument.DOM.documentElement.selectSingleNode("/po:purchaseOrder/po:shipToSubmitter");
  55.         
  56.     if (xmlDeliverTo == null)
  57.     {
  58.         setNodeTypedValue(xmlShipToSubmitter, "true");
  59.     }
  60.     else
  61.     {
  62.         setNodeTypedValue(xmlShipToSubmitter, "false");
  63.     }
  64. }
  65.  
  66.  
  67. /*------------------------------------------------------------------------------
  68.     This function handler is generated and managed automatically.
  69.     Do not rename the function or alter its parameter list.
  70. ------------------------------------------------------------------------------*/
  71. function msoxd__currency::OnAfterChange(oEvent)
  72. {
  73.     // Avoid side effects when DOM is read only.
  74.     if (XDocument.IsDOMReadOnly)
  75.         return;
  76.  
  77.     updateCurrency(oEvent.Site.parentNode, "po:");
  78.     return true;
  79. }
  80.  
  81.  
  82. /*------------------------------------------------------------------------------
  83.     This function handler is generated and managed automatically.
  84.     Do not rename the function or alter its parameter list.
  85. ------------------------------------------------------------------------------*/
  86. function msoxd__shipToSubmitter::OnAfterChange(oEvent)
  87. {
  88.     // Avoid side effects when DOM is read only.
  89.     if (XDocument.IsDOMReadOnly)
  90.         return;
  91.  
  92.     var xmlDeliverTo = getNode("/po:purchaseOrder/po:deliverTo");
  93.     
  94.     if (oEvent.Site.nodeTypedValue == "true")
  95.     {
  96.         if (xmlDeliverTo != null)
  97.         {
  98.             g_xmlDeliverTo = xmlDeliverTo.parentNode.removeChild(xmlDeliverTo);
  99.         }
  100.     }
  101.     else
  102.     {
  103.         xmlNode = XDocument.DOM.documentElement.selectSingleNode("/po:purchaseOrder/po:suggestedSuppliers");
  104.         if (xmlNode != null && xmlDeliverTo == null)
  105.         {
  106.             xmlNode.parentNode.insertBefore(g_xmlDeliverTo, xmlNode);
  107.         }
  108.     }
  109. }
  110.  
  111.  
  112. /*------------------------------------------------------------------------------
  113.     This function handler is generated and managed automatically.
  114.     Do not rename the function or alter its parameter list.
  115. ------------------------------------------------------------------------------*/
  116. function msoxd__item::OnAfterChange(oEvent)
  117. {
  118.     var strChangedNode = normalizeSource(oEvent).nodeName;
  119.  
  120.     // Avoid side effects when DOM is read only.
  121.     if (XDocument.IsDOMReadOnly)
  122.         return;
  123.  
  124.     // check to see if we care about what was changed
  125.     if (strChangedNode != "po:actualQuantity" 
  126.         && strChangedNode != "po:actualUnitPrice"
  127.         && strChangedNode != "po:actualDiscount"        
  128.         && strChangedNode != "po:actualTaxRate"
  129.         && strChangedNode != "po:item")
  130.     {
  131.         return;
  132.     }
  133.  
  134.     var xmlItems = XDocument.DOM.documentElement.selectSingleNode("/po:purchaseOrder/po:order/po:items");
  135.  
  136.     calculateItem(oEvent.Site);
  137. }
  138.  
  139.  
  140. /*------------------------------------------------------------------------------
  141.     This function handler is generated and managed automatically.
  142.     Do not rename the function or alter its parameter list.
  143. ------------------------------------------------------------------------------*/
  144. function msoxd__freight::OnAfterChange(oEvent)
  145. {
  146.     // Avoid side effects when DOM is read only.
  147.     if (XDocument.IsDOMReadOnly)
  148.         return;
  149.  
  150.     calculateTotals();
  151. }
  152.  
  153.  
  154. /*==============================================================================
  155.     Private functions
  156. ==============================================================================*/
  157.  
  158. function calculateItem(xmlItem)
  159. {
  160.     var nQuantity = 0;
  161.     var nUnitPrice = 0;
  162.     var nDiscount = 0;
  163.     var nTaxRate = 0;
  164.     var nPreTaxPrice = 0;
  165.     var nExtendedPrice = 0;
  166.     var xmlPreTaxPrice;
  167.  
  168.     nQuantity = getNodeTypedValue(xmlItem.selectSingleNode("po:actualQuantity"), 0, true);
  169.     nUnitPrice = getNodeTypedValue(xmlItem.selectSingleNode("po:actualUnitPrice"), 0, true);
  170.     nDiscount = getNodeTypedValue(xmlItem.selectSingleNode("po:actualDiscount"), 0, true);
  171.     nTaxRate = getNodeTypedValue(xmlItem.selectSingleNode("po:actualTaxRate"), 0, true);
  172.  
  173.     xmlPreTaxPrice = xmlItem.selectSingleNode("po:actualPreTaxPrice");
  174.     xmlExtendedPrice = xmlItem.selectSingleNode("po:actualExtendedPrice");
  175.  
  176.     nPreTaxPrice = (nQuantity * nUnitPrice) - nDiscount;
  177.     nExtendedPrice = nPreTaxPrice * (1 + nTaxRate);
  178.  
  179.     setRoundedValue(xmlPreTaxPrice, nPreTaxPrice);
  180.     setRoundedValue(xmlExtendedPrice, nExtendedPrice);
  181.  
  182.     calculateTotals();
  183. }
  184.  
  185.  
  186. function calculateTotals()
  187. {
  188.     var xmlDom = XDocument.DOM;
  189.     var nSubtotal = 0;
  190.     var nTaxes = 0;
  191.     var nDiscount = 0;
  192.     var nFreight = 0;
  193.     var nTotal = 0;
  194.  
  195.     var xmlSubtotal;
  196.     var xmlTaxes;
  197.     var xmlTotal;
  198.     var xmlItems;
  199.     var xmlItemsTax;
  200.  
  201.     xmlSubtotal = xmlDom.selectSingleNode("/po:purchaseOrder/po:order/po:actualTotal/po:subtotal");
  202.     xmlTaxes = xmlDom.selectSingleNode("/po:purchaseOrder/po:order/po:actualTotal/po:taxes");
  203.     xmlTotal = xmlDom.selectSingleNode("/po:purchaseOrder/po:order/po:actualTotal/po:total");
  204.  
  205.     xmlItems = xmlDom.selectNodes("/po:purchaseOrder/po:order/po:items/po:item/po:actualPreTaxPrice")
  206.     xmlItemsTax = xmlDom.selectNodes("/po:purchaseOrder/po:order/po:items/po:item/po:actualExtendedPrice")
  207.  
  208.     nDiscount = getNodeTypedValue("/po:purchaseOrder/po:order/po:actualTotal/po:discount", 0, true);
  209.     nFreight = getNodeTypedValue("/po:purchaseOrder/po:order/po:actualTotal/po:freight", 0, true);
  210.  
  211.     nSubtotal = sum(xmlItems);
  212.     nTotal = sum(xmlItemsTax);
  213.     nTaxes = nTotal - nSubtotal;
  214.     nTotal = nTotal + nFreight - nDiscount;
  215.  
  216.     setRoundedValue(xmlSubtotal, nSubtotal);
  217.     setRoundedValue(xmlTaxes, nTaxes);
  218.     setRoundedValue(xmlTotal, nTotal);
  219. }
  220.  
  221. /*------------------------------------------------------------------------------
  222.  
  223.     Common form template functions section - the following code is common to all out-of-box form templates.
  224.  
  225. ------------------------------------------------------------------------------*/
  226.  
  227. /* =============================================================================
  228.     Currency handling
  229. ============================================================================= */
  230.  
  231. /*------------------------------------------------------------------------------
  232.     updateCurrency()
  233. ------------------------------------------------------------------------------*/
  234. function updateCurrency(xmlNode, strPrefix)
  235. {
  236.     var xmlName = xmlNode.selectSingleNode(strPrefix + 'name');
  237.     var xmlSymbol = xmlNode.selectSingleNode(strPrefix + 'symbol');
  238.  
  239.     var strName = xmlName.text;
  240.     var strSymbol = lookupCurrencySymbol(strName);
  241.  
  242.     if (strSymbol)
  243.     {
  244.         setNodeValue(xmlSymbol, '(' + strSymbol + ') ');
  245.     }
  246.     else
  247.     {
  248.         setNodeValue(xmlName, '');
  249.         setNodeValue(xmlSymbol, '');
  250.     }
  251. }
  252.  
  253. /*------------------------------------------------------------------------------
  254.     lookupCurrencySymbol()
  255. ------------------------------------------------------------------------------*/
  256. function lookupCurrencySymbol(strName)
  257. {
  258.     var xmlCurDom = XDocument.GetDOM("currencies");
  259.     var xmlCurrency = xmlCurDom.selectSingleNode("/currencies/currency[@name = '" + strName + "']");
  260.  
  261.     if (xmlCurrency)
  262.         return xmlCurrency.getAttribute("symbol");
  263. }
  264.  
  265. /* =============================================================================
  266.     Date/Time handling
  267. ============================================================================= */
  268.  
  269. /*------------------------------------------------------------------------------
  270.     getDateString()
  271. ------------------------------------------------------------------------------*/
  272. function getDateString(oDate)
  273. {
  274.     // Use today as default value.
  275.     if (oDate == null)
  276.         oDate = new Date();
  277.  
  278.     var m = oDate.getMonth() + 1;
  279.     var d = oDate.getDate();
  280.  
  281.     if (m < 10)
  282.         m = "0" + m;
  283.  
  284.     if (d < 10)
  285.         d = "0" + d;
  286.  
  287.     // ISO 8601 date (YYYY-MM-DD).
  288.     return oDate.getFullYear() + "-" + m + "-" + d;
  289. }
  290.  
  291. /*------------------------------------------------------------------------------
  292.     getTimeString()
  293. ------------------------------------------------------------------------------*/
  294. function getTimeString(oTime)
  295. {
  296.     // Use now as default value.
  297.     if (oTime == null)
  298.         oTime = new Date();
  299.  
  300.     var h = oTime.getHours();
  301.     var m = oTime.getMinutes();
  302.     var s = oTime.getSeconds();
  303.     var ms = oTime.getMilliseconds();
  304.  
  305.     if (h < 10)
  306.         h = "0" + h;
  307.  
  308.     if (m < 10)
  309.         m = "0" + m;
  310.  
  311.     if (s < 10)
  312.         s = "0" + s;
  313.  
  314.     if (ms < 100)
  315.         ms = "0" + (ms < 10 ? "0" : "") + ms;
  316.  
  317.     // ISO 8601 time (HH:MM:SS.SSS).
  318.     return h + ":" + m + ":" + s + "." + ms;
  319. }
  320. /* =============================================================================
  321.     Language conversion operations    
  322. ============================================================================= */
  323.  
  324. /*------------------------------------------------------------------------------
  325.     convertXMLNumberToJScript()
  326. ------------------------------------------------------------------------------*/
  327. function convertXMLNumberToJScript(value)
  328. {
  329.     var retVal;
  330.  
  331.     switch (value)
  332.     {
  333.         case "-INF":
  334.             retVal = Number.NEGATIVE_INFINITY;
  335.             break;
  336.  
  337.         case "INF":
  338.             retVal = Number.POSITIVE_INFINITY;
  339.             break;
  340.  
  341.         case "NaN":
  342.             retVal = NaN;
  343.             break;
  344.  
  345.         default:
  346.             //If no prefix of its argument can be parsed as a
  347.             //float, parseFloat will return NaN. Otherwise, it
  348.             //will return the value of the longest float prefix
  349.             //it can find in its argument.
  350.             retVal = parseFloat(value);
  351.     }
  352.  
  353.     return retVal;
  354. }
  355.  
  356. /*------------------------------------------------------------------------------
  357.     convertJScriptNumberToXML()
  358. ------------------------------------------------------------------------------*/
  359. function convertJScriptNumberToXML(value)
  360. {
  361.     var retVal;
  362.  
  363.     switch (value)
  364.     {
  365.         case Number.NEGATIVE_INFINITY:
  366.             retVal = "-INF";
  367.             break;
  368.  
  369.         case Number.POSITIVE_INFINITY:
  370.             retVal = "INF";
  371.             break;
  372.  
  373.         //NaN == NaN returns false. Any other value is equal to itself,
  374.         //which is why the case below differentiates between NaN and
  375.         //any other valid value
  376.         case value:
  377.             retVal = value;
  378.             break;
  379.  
  380.         default:
  381.             retVal = "NaN";
  382.     }
  383.  
  384.     return retVal;
  385. }
  386.  
  387. /* =============================================================================
  388.     Node value operations
  389. ============================================================================= */
  390.  
  391. /*------------------------------------------------------------------------------
  392.     isInvalidOrEmpty()
  393. ------------------------------------------------------------------------------*/
  394. function isInvalidOrEmpty(xmlNode)
  395. {
  396.     // If there is no value, ignore it.
  397.     if (!xmlNode || !xmlNode.text)
  398.         return true;
  399.  
  400.     // The caller can pass additional error types as optional arguments.
  401.     var aErrorTypes = new Array;
  402.     if (arguments.length > 1)
  403.     {
  404.         for (var i=1; i<arguments.length; i++)
  405.             aErrorTypes.push(arguments[i]);
  406.     }
  407.     else
  408.     {
  409.         aErrorTypes.push("SCHEMA_VALIDATION");
  410.     }
  411.  
  412.     // If there is a validation error related to this node,
  413.     // then the node is invalid.
  414.     for (var i=0; i<XDocument.Errors.Count; i++)
  415.     {
  416.         var oError = XDocument.Errors(i);
  417.  
  418.         if (xmlNode == oError.Node)
  419.         {
  420.             for (var j in aErrorTypes)
  421.             {
  422.                 if (oError.Type == aErrorTypes[j])
  423.                     return true;
  424.             }
  425.         }
  426.     }
  427.  
  428.     // Is valid (no error was found).
  429.     return false;
  430. }
  431.  
  432. /*------------------------------------------------------------------------------
  433.     getNodeValue()
  434. ------------------------------------------------------------------------------*/
  435. function getNodeValue(xpath, defaultValue)
  436. {
  437.     var xmlNode = getNode(xpath);
  438.  
  439.     if (isInvalidOrEmpty(xmlNode))
  440.         return (arguments.length > 1) ? defaultValue : "";
  441.     else
  442.         return xmlNode.text;
  443. }
  444.  
  445. /*------------------------------------------------------------------------------
  446.     getNodeTypedValue()    - Use instead of getNodeValue() if you want
  447.         to parse the value by MSXML according to data type (from schema).
  448.         
  449.     NOTE: this is not really useful for dates in Jscript.
  450. ------------------------------------------------------------------------------*/
  451. function getNodeTypedValue(xpath, defaultValue, fNumber)
  452. {
  453.     var xmlNode = getNode(xpath);
  454.  
  455.     if (isInvalidOrEmpty(xmlNode))
  456.         return (arguments.length > 1) ? defaultValue : "";
  457.     
  458.     if (fNumber)
  459.         return convertXMLNumberToJScript(xmlNode.nodeTypedValue);
  460.     else
  461.         return xmlNode.nodeTypedValue;
  462. }
  463.  
  464. /*------------------------------------------------------------------------------
  465.     setNodeValue()
  466. ------------------------------------------------------------------------------*/
  467. function setNodeValue(xpath, value)
  468. {
  469.     var xmlNode = getNode(xpath);
  470.  
  471.     if (!xmlNode)
  472.         return;
  473.  
  474.     // The xsi:nil needs to be removed before we set the value.
  475.     if (value != "" && xmlNode.getAttribute("xsi:nil"))
  476.         xmlNode.removeAttribute("xsi:nil");
  477.  
  478.     // Setting the value would mark the document as dirty.
  479.     // Let's do that if the value has really changed.
  480.     if (xmlNode.text != value)
  481.         xmlNode.text = value;
  482. }
  483.  
  484. /*------------------------------------------------------------------------------
  485.     setNodeTypedValue() - Use instead of setNodeValue() if you want
  486.         to format the value by MSXML according to data type (from schema).
  487.         
  488.         Use this when setting floating point and decimal values. These
  489.         values are formatted differently in some locales and will cause schema
  490.         validation errors when set using setNodeValue or a node's text property.
  491.         
  492.     NOTE: this is not really useful for dates in Jscript.
  493. ------------------------------------------------------------------------------*/
  494. function setNodeTypedValue(xpath, value)
  495. {
  496.     var xmlNode = getNode(xpath);
  497.     
  498.     if (!xmlNode)
  499.         return;
  500.  
  501.     // The xsi:nil needs to be removed before we set the value.
  502.     if (value != "" && xmlNode.getAttribute("xsi:nil"))
  503.         xmlNode.removeAttribute("xsi:nil");
  504.  
  505.     var convertedValue = convertJScriptNumberToXML(value);
  506.  
  507.     // Setting the value would mark the document as dirty.
  508.     // Let's do that if the value has really changed.
  509.     if (xmlNode.nodeTypedValue != convertedValue)
  510.         xmlNode.nodeTypedValue = convertedValue;
  511. }
  512.  
  513. /*------------------------------------------------------------------------------
  514.     setRoundedValue() - Rounds the value to the default number of decimal
  515.     places and calls setNodeTypedValue() with the result.
  516. ------------------------------------------------------------------------------*/
  517. function setRoundedValue(xpath, value)
  518. {
  519.     var xmlNode = getNode(xpath);
  520.  
  521.     if (!xmlNode)
  522.         return;
  523.  
  524.     var roundedValue = roundFloat(value, 3);
  525.     setNodeTypedValue(xpath, roundedValue);
  526. }
  527.  
  528. /*------------------------------------------------------------------------------
  529.     setNil() - Empty a nillable element.
  530. ------------------------------------------------------------------------------*/
  531. function setNil(xpath)
  532. {
  533.     var xmlNode = getNode(xpath);
  534.  
  535.     if (!xmlNode || xmlNode.text === "")
  536.         return;
  537.  
  538.     // Create xsi:nil attribute with the proper namespace.
  539.     var xmlNil = xmlNode.ownerDocument.createNode(2, "xsi:nil", "http://www.w3.org/2001/XMLSchema-instance");
  540.     xmlNil.text = "true";
  541.  
  542.     // The order is important.
  543.     xmlNode.text = "";
  544.     xmlNode.setAttributeNode(xmlNil);
  545. }
  546.  
  547. /*------------------------------------------------------------------------------
  548.     roundFloat() - Rounds the value to the specified number of decimal places.
  549. ------------------------------------------------------------------------------*/
  550. function roundFloat(value, decimalPlaces)
  551. {
  552.     if (value < -1E15 || 1E15 < value)
  553.     {
  554.         return value;
  555.     }
  556.     else
  557.     {
  558.         var nPowerToRound = Math.pow(10, decimalPlaces);
  559.         return Math.round(value*nPowerToRound)/nPowerToRound;
  560.     }
  561. }
  562.  
  563. /*------------------------------------------------------------------------------
  564.     normalizeSource() - Ensures that we always get a node of type NODE_ELEMENT
  565.         or NODE_ATTRIBUTE.
  566. ------------------------------------------------------------------------------*/
  567. function normalizeSource(oEvent)
  568. {
  569.     // Return the node's parent if the node is of type NODE_TEXT and its parent
  570.     // is of type NODE_ELEMENT
  571.     if (oEvent.Parent.nodeType == 1 && oEvent.Source.nodeType == 3)
  572.         return oEvent.Parent;
  573.         
  574.     // Return the node if it is of type NODE_ELEMENT or NODE_ATTRIBUTE;
  575.     if (oEvent.Source.nodeType == 1 || oEvent.Source.nodeType == 2)
  576.         return oEvent.Source;
  577. }
  578.  
  579. /*------------------------------------------------------------------------------
  580.     normalizeParent() - Ensures that we always get the parent of a node of 
  581.         type NODE_ELEMENT or NODE_ATTRIBUTE.
  582. ------------------------------------------------------------------------------*/
  583. function normalizeParent(oEvent)
  584. {
  585.     // Return the node if it is of type NODE_ELEMENT or NODE_ATTRIBUTE;
  586.     if (oEvent.Source.nodeType == 1 || oEvent.Source.nodeType == 2)
  587.         return oEvent.Parent;
  588.         
  589.     // Return the node 's parent if the node is of type NODE_TEXT and its parent
  590.     // is of type NODE_ELEMENT
  591.     if (oEvent.Parent.nodeType == 1 && oEvent.Source.nodeType == 3)
  592.         return oEvent.Parent.parentNode;
  593. }
  594.  
  595. /*------------------------------------------------------------------------------
  596.     initializeNodeValue()
  597. ------------------------------------------------------------------------------*/
  598. function initializeNodeValue(xpath, strValue)
  599. {
  600.     var xmlNode = getNode(xpath);
  601.  
  602.     // Set the node value *ONLY* if the node is empty.
  603.     if (xmlNode.text == "")
  604.         setNodeValue(xmlNode, strValue);
  605. }
  606.  
  607. /* =============================================================================
  608.     Simple nodelist operations
  609. ============================================================================= */
  610.  
  611. /*------------------------------------------------------------------------------
  612.     getNode()
  613. ------------------------------------------------------------------------------*/
  614. function getNode(xpath)
  615. {
  616.     // Both XML node and absolute XPath are allowed.
  617.     if (typeof(xpath) == "string")
  618.         return XDocument.DOM.selectSingleNode(xpath);
  619.     else
  620.         return xpath;
  621. }
  622.  
  623. /*------------------------------------------------------------------------------
  624.     getNodeList()
  625. ------------------------------------------------------------------------------*/
  626. function getNodeList(xpath)
  627. {
  628.     // Both XML node and absolute XPath are allowed.
  629.     if (typeof(xpath) == "string")
  630.         return XDocument.DOM.selectNodes(xpath);
  631.     else
  632.         return xpath;
  633. }
  634.  
  635. /*------------------------------------------------------------------------------
  636.     count()
  637. ------------------------------------------------------------------------------*/
  638. function count(xpath)
  639. {
  640.     var xmlNodeList = getNodeList(xpath);
  641.  
  642.     if (xmlNodeList)
  643.         return xmlNodeList.length;
  644.     else
  645.         return -1;
  646. }
  647.  
  648. /* =============================================================================
  649.     Complex nodelist operations
  650. ============================================================================= */
  651.  
  652. /*------------------------------------------------------------------------------
  653.     applyAction() - Iterate the whole nodelist and apply a given action
  654.         to each node in the nodelist. You can implement your own actions.
  655.  
  656.     Example:
  657.         var oAction = new Object();
  658.         oAction.sum = 0;
  659.         oAction.apply = new Function("xmlNode", "this.sum += xmlNode.nodeTypedValue");
  660. ------------------------------------------------------------------------------*/
  661. function applyAction(xpath, oAction)
  662. {
  663.     var xmlNodeList = getNodeList(xpath);
  664.     var xmlNode;
  665.  
  666.     while (xmlNode = xmlNodeList.nextNode())
  667.         oAction.apply(xmlNode);
  668.  
  669.     xmlNodeList.reset();
  670. }
  671.  
  672. /*------------------------------------------------------------------------------
  673.     sum()
  674. ------------------------------------------------------------------------------*/
  675. function sum(xpath)
  676. {
  677.     var oAction = new Object;
  678.     oAction.nResult = 0;
  679.     oAction.apply = new Function("xmlNode", "this.nResult += getNodeTypedValue(xmlNode, 0, true)");
  680.  
  681.     applyAction(xpath, oAction);
  682.     return oAction.nResult;
  683. }
  684.  
  685. /*------------------------------------------------------------------------------
  686.     anyInvalidOrEmpty() - Searches a nodelist to see if there are any invalid 
  687.         or empty nodes.
  688. ------------------------------------------------------------------------------*/
  689. function anyInvalidOrEmpty(xpath)
  690. {
  691.     var oAction = new Object;
  692.     oAction.fInvalid = false;
  693.     oAction.apply = new Function("xmlNode", "this.fInvalid |= isInvalidOrEmpty(xmlNode)");
  694.     
  695.     applyAction(xpath, oAction);
  696.     return oAction.fInvalid;
  697. }
  698.  
  699. /*------------------------------------------------------------------------------
  700.     reindex()
  701. ------------------------------------------------------------------------------*/
  702. function reindex(xpath, iStart)
  703. {
  704.     if (iStart == null)
  705.         iStart = 1;
  706.  
  707.     var oAction = new Object;
  708.     oAction.i = iStart;
  709.     oAction.apply = new Function("xmlNode", "setNodeTypedValue(xmlNode, this.i++)");
  710.  
  711.     return applyAction(xpath, oAction);
  712. }
  713.  
  714. /*------------------------------------------------------------------------------
  715.     average()
  716. ------------------------------------------------------------------------------*/
  717. function average(xpath, xpathWeights)
  718. {
  719.     var oAction = new Object;
  720.     oAction.nSum = 0;
  721.     oAction.cNodes = 0;
  722.  
  723.     if (!xpathWeights)
  724.     {
  725.         oAction.apply = actionSimpleAverage;
  726.         oAction.result = resultSimpleAverage;
  727.     }
  728.     else
  729.     {
  730.         oAction.nWeights = 0;
  731.         oAction.xmlWeights = getNodeList(xpathWeights);
  732.         oAction.apply = actionWeightedAverage;
  733.         oAction.result = resultWeightedAverage;
  734.     }
  735.  
  736.     applyAction(xpath, oAction);
  737.     return oAction.result();
  738. }
  739.  
  740. /*------------------------------------------------------------------------------
  741.     actionSimpleAverage()
  742. ------------------------------------------------------------------------------*/
  743. function actionSimpleAverage(xmlNode)
  744. {
  745.     if (!isInvalidOrEmpty(xmlNode))
  746.     {
  747.         this.nSum += getNodeTypedValue(xmlNode, 0, true);
  748.         this.cNodes++;
  749.     }
  750. }
  751.  
  752. /*------------------------------------------------------------------------------
  753.     resultSimpleAverage()
  754. ------------------------------------------------------------------------------*/
  755. function resultSimpleAverage()
  756. {
  757.     if (this.cNodes)
  758.         return this.nSum / this.cNodes;
  759.     else
  760.         return 0;
  761. }
  762.  
  763. /*------------------------------------------------------------------------------
  764.     actionWeightedAverage()
  765. ------------------------------------------------------------------------------*/
  766. function actionWeightedAverage(xmlNode)
  767. {
  768.     // There should be one weight for each node. Even if the node is invalid,
  769.     // we have to advance the iterator of weights to keep them in sync.
  770.     var xmlWeight = this.xmlWeights.nextNode();
  771.  
  772.     if (!isInvalidOrEmpty(xmlNode) && !isInvalidOrEmpty(xmlWeight))
  773.     {
  774.         var nValue = getNodeTypedValue(xmlNode, 0, true);
  775.         var nWeight = getNodeTypedValue(xmlWeight, 1, true);
  776.  
  777.         this.nSum += nValue * nWeight;
  778.         this.nWeights += nWeight;
  779.         this.cNodes++;
  780.     }
  781. }
  782.  
  783. /*------------------------------------------------------------------------------
  784.     resultWeightedAverage()
  785. ------------------------------------------------------------------------------*/
  786. function resultWeightedAverage()
  787. {
  788.     this.xmlWeights.reset();
  789.  
  790.     if (this.cNodes)
  791.     {
  792.         if (this.nWeights > 0)
  793.             return this.nSum / this.nWeights;
  794.         else
  795.             return this.nSum
  796.     }
  797.     else
  798.     {
  799.         return 0;
  800.     }
  801. }
  802.  
  803. /*------------------------------------------------------------------------------
  804.     sort()
  805. ------------------------------------------------------------------------------*/
  806. function sort(xpathParent, xpathChildren, fnCompare)
  807. {
  808.     // Retrieve the parent node; all the children will be sorted.
  809.     var xmlOrigParent = getNode(xpathParent)
  810.     var xmlItems = xmlOrigParent.selectNodes(xpathChildren);
  811.  
  812.     if (1 < count(xmlItems))
  813.     {
  814.         // Store the (pointers to) items in an array for faster access.
  815.         rgItems = new Array();
  816.         
  817.         while (xmlItem = xmlItems.nextNode())
  818.             rgItems.push(xmlItem);
  819.             
  820.         // Sort the array (the XML does not change).
  821.         rgItems.sort(fnCompare);
  822.  
  823.         // Now that the array is sorted the DOM should be updated.
  824.         var xmlSortParent = xmlOrigParent.cloneNode(true);
  825.         var xmlClones = xmlSortParent.selectNodes(xpathChildren);
  826.         xmlClones.removeAll();
  827.         
  828.         // Update the nodelist, each item is moved only once.
  829.         // (each change is causing several notifications to be fired.)
  830.         for (var i=0; i<rgItems.length; i++)
  831.             xmlSortParent.appendChild(rgItems[i].cloneNode(true));
  832.         
  833.         xmlOrigParent.parentNode.replaceChild(xmlSortParent, xmlOrigParent);
  834.     }
  835. }
  836.